package demo

import (
	crand "crypto/rand"
	"encoding/base64"
	"fmt"
	"log"
	"math/rand"
	"mit-6.824/src/labrpc"
	"time"
)

/**
全局的上下文
 */
type context struct {
n         int
rafts     []*Node
connected []bool   // whether each server is on the net
net       *labrpc.Network
endnames  [][]string            // the port file names each sends to
}

func initAndStart(n int) *context {
	ctx:=&context{}
	ctx.n=3
	ctx.rafts=make([]*Node,ctx.n)
	ctx.connected=make([]bool,ctx.n)
	ctx.endnames=make([][]string,ctx.n)
	ctx.net = labrpc.MakeNetwork()

	//开启启动每一个Node
	for i := 0; i < ctx.n; i++ {
		//初始化每个节点，并加载
		ctx.loadNode(i);
		ctx.connect(i);
	}


	return ctx;
}

func (ctx *context) loadNode(curIdx int)   {
	ctx.endnames[curIdx] = make([]string, ctx.n)
	for j := 0; j < ctx.n; j++ {
		ctx.endnames[curIdx][j] = randstring(10)
	}
	//创建ends 网络请求
	ends :=make([]*labrpc.ClientEnd,ctx.n)
	for j := 0; j < ctx.n; j++ {
		ends[j] = ctx.net.MakeEnd(ctx.endnames[curIdx][j])
		ctx.net.Connect(ctx.endnames[curIdx][j], j)
	}
	// 创建通道接收来自其他节点的日志信息
	applyCh := make(chan ApplyMsg)

	go func() {
		for m := range applyCh{
			log.Printf("接收到日志消息 %v",m)
		}
	}()

	node:=Init(ends,curIdx,applyCh)
	ctx.rafts[curIdx]=node


	//拉起服务
	svc := labrpc.MakeService(node)
	srv := labrpc.MakeServer()
	srv.AddService(svc)
	ctx.net.AddServer(curIdx, srv)


}

func (cfg *context) connect(i int) {

	cfg.connected[i] = true

	// outgoing ClientEnds
	for j := 0; j < cfg.n; j++ {
		if cfg.connected[j] {
			endname := cfg.endnames[i][j]
			cfg.net.Enable(endname, true)
		}
	}

	// incoming ClientEnds
	for j := 0; j < cfg.n; j++ {
		if cfg.connected[j] {
			endname := cfg.endnames[j][i]
			cfg.net.Enable(endname, true)
		}
	}
}


func randstring(n int) string {
	b := make([]byte, 2*n)
	crand.Read(b)
	s := base64.URLEncoding.EncodeToString(b)
	return s[0:n]
}

func (cfg *context) checkTerms() int {
	term := -1
	for i := 0; i < cfg.n; i++ {
		if cfg.connected[i] {
			xterm, _ := cfg.rafts[i].GetState()
			if term == -1 {
				term = xterm
			} else if term != xterm {
				fmt.Errorf("servers disagree on term")
			}
		}
	}
	return term
}
func (cfg *context) checkOneLeader() int {
	for iters := 0; iters < 10; iters++ {
		ms := 450 + (rand.Int63() % 100)
		time.Sleep(time.Duration(ms) * time.Millisecond)

		leaders := make(map[int][]int)
		for i := 0; i < cfg.n; i++ {
			if cfg.connected[i] {
				if term, leader := cfg.rafts[i].GetState(); leader {
					leaders[term] = append(leaders[term], i)
				}
			}
		}

		lastTermWithLeader := -1
		for term, leaders := range leaders {
			if len(leaders) > 1 {
				fmt.Errorf("term %d has %d (>1) leaders", term, len(leaders))
			}
			if term > lastTermWithLeader {
				lastTermWithLeader = term
			}
		}

		if len(leaders) != 0 {
			return leaders[lastTermWithLeader][0]
		}
	}
	fmt.Errorf("expected one leader, got none")
	return -1
}

